import Layer from '../../../core/Layer';
import ma from '../../../quota/ma';
import boll from '../../../quota/boll';
import MultiLine from '../../../base/MultiLine';
import Point from '../../../core/Point';
import env from '../../../quota/env';
import mike from '../../../quota/mike';
import sar from '../../../quota/sar';
import Circle from '../../../base/Circle';
const indicatorFunctions = {
    ma(layer, data, start, end) {
        const { N1 = 5, N2 = 10, N3 = 20 } = layer.params;
        let dataStart = start - N3;
        if (dataStart < 0) {
            dataStart = 0;
        }
        const close = [];
        for (let i = dataStart; i < end; i++) {
            close.push(parseFloat(data[i].close));
        }
        const ma5 = ma(close, N1);
        const ma10 = ma(close, N2);
        const ma20 = ma(close, N3);
        const dataLen = (ma20.length > end - start) ? (end - start) : ma20.length;
        let ma5Data = [], ma10Data = [], ma20Data = [];
        let max = ma5[ma5.length - dataLen];
        let min = ma5[ma5.length - dataLen];
        for (let i = 1; i <= dataLen; i++) {
            if (max < ma5[ma5.length - i]) {
                max = ma5[ma5.length - i];
            }
            if (min > ma5[ma5.length - i]) {
                min = ma5[ma5.length - i];
            }
            if (max < ma10[ma10.length - i]) {
                max = ma10[ma10.length - i];
            }
            if (min > ma10[ma10.length - i]) {
                min = ma10[ma10.length - i];
            }
            if (max < ma20[ma20.length - i]) {
                max = ma20[ma20.length - i];
            }
            if (min > ma20[ma20.length - i]) {
                min = ma20[ma20.length - i];
            }
            ma5Data.unshift(ma5[ma5.length - i]);
            ma10Data.unshift(ma10[ma10.length - i]);
            ma20Data.unshift(ma20[ma20.length - i]);
        }
        const drawFun = (indicators, layer, options) => {
            const { ma5, ma10, ma20 } = indicators;
            const { xStep, yStep, xBase } = options;
            let ma5Points = [];
            let ma10Points = [];
            let ma20Points = [];
            for (let i = 0; i < ma5.length; i++) {
                ma5Points.push(new Point(i * xStep + layer.position.x, (ma5[i] - xBase) * yStep + layer.position.y));
                ma10Points.push(new Point(i * xStep + layer.position.x, (ma10[i] - xBase) * yStep + layer.position.y));
                ma20Points.push(new Point(i * xStep + layer.position.x, (ma20[i] - xBase) * yStep + layer.position.y));
            }
            let ma5Line = new MultiLine(layer.canvas, {
                color: '#1DA343',
                lineWidth: 2,
            }, ma5Points);
            let ma10Line = new MultiLine(layer.canvas, {
                color: '#C32512',
                lineWidth: 2
            }, ma10Points);
            let ma20Line = new MultiLine(layer.canvas, {
                color: '#F68e12',
                lineWidth: 2,

            }, ma20Points);
            layer.addChild(ma5Line, ma10Line, ma20Line);
        }

        return {
            max,
            min,
            drawFun,
            indicators: {
                ma5: ma5Data,
                ma10: ma10Data,
                ma20: ma20Data,
            },
        };

    },
    boll(layer, data, start, end) {
        const { N = 20, width = 2 } = layer.params;
        let dataStart = start - N;
        if (dataStart < 0) {
            dataStart = 0;
        }
        const close = [];
        for (let i = dataStart; i < end; i++) {
            close.push(parseFloat(data[i].close));
        }
        const { middle, upper, low } = boll(close, N, width);
        const dataLen = (middle.length > end - start) ? (end - start) : middle.length;
        let min = middle[middle.length - 1];
        let max = middle[middle.length -1];
        const middleData = [], upperData = [], lowData = [];
        for (let i = 1; i <= dataLen; i++) {
            if (min > low[low.length - i]) {
                min = low[low.length - i];
            }
            if (max < upper[upper.length - i]) {
                max = upper[upper.length - i];
            }
            middleData.unshift(middle[middle.length - i]);
            upperData.unshift(upper[upper.length - i]);
            lowData.unshift(low[low.length - i]);
        }
        const drawFun = (indicators, layer, options) => {
            const { upper, middle, low } = indicators;
            const { xStep, yStep, xBase } = options;
            let upperPoints = [];
            let middlePoints = [];
            let lowPoints = [];
            for (let i = 0; i < upper.length; i++) {
                upperPoints.push(new Point(i * xStep + layer.position.x, (upper[i] - xBase) * yStep + layer.position.y));
                middlePoints.push(new Point(i * xStep + layer.position.x, (middle[i] - xBase) * yStep + layer.position.y));
                lowPoints.push(new Point(i * xStep + layer.position.x, (low[i] - xBase) * yStep + layer.position.y));
            }
            let upperLine = new MultiLine(layer.canvas, {
                color: '#C32512',
                lineWidth: 2,
            }, upperPoints);
            let middleLine = new MultiLine(layer.canvas, {
                color: '#136583',
                lineWidth: 2,
            }, middlePoints);
            let lowLine = new MultiLine(layer.canvas, {
                color: '#1DA343',
                lineWidth: 2,
            }, lowPoints);
            layer.addChild(upperLine, middleLine, lowLine);
        };
        return {
            max,
            min,
            drawFun,
            indicators: {
                upper: upperData,
                low: lowData,
                middle: middleData
            },
        };
    },
    env(layer, data, start, end) {
        const { N = 20, width = 0.04 } = layer.params;
        let dataStart = start - N;
        if (dataStart < 0) {
            dataStart = 0;
        }
        const close = [];
        for (let i = dataStart; i < end; i++) {
            close.push(parseFloat(data[i].close));
        }
        const { UpperLine, MiddleLine, LowerLine } = env(close, width, N);
        const dataLen = (MiddleLine.length > end - start) ? (end - start) : MiddleLine.length;
        let min = MiddleLine[MiddleLine.length - 1];
        let max = MiddleLine[MiddleLine.length -1];
        const middleData = [], upperData = [], lowData = [];
        for (let i = 1; i <= dataLen; i++) {
            if (min > LowerLine[LowerLine.length - i]) {
                min = LowerLine[LowerLine.length - i];
            }
            if (max < UpperLine[UpperLine.length - i]) {
                max = UpperLine[UpperLine.length - i];
            }
            middleData.unshift(MiddleLine[MiddleLine.length - i]);
            upperData.unshift(UpperLine[UpperLine.length - i]);
            lowData.unshift(LowerLine[LowerLine.length - i]);
        }
        const drawFun = (indicators, layer, options) => {
            const { upper, middle, low } = indicators;
            const { xStep, yStep, xBase } = options;
            let upperPoints = [];
            let middlePoints = [];
            let lowPoints = [];
            for (let i = 0; i < upper.length; i++) {
                upperPoints.push(new Point(i * xStep + layer.position.x, (upper[i] - xBase) * yStep + layer.position.y));
                middlePoints.push(new Point(i * xStep + layer.position.x, (middle[i] - xBase) * yStep + layer.position.y));
                lowPoints.push(new Point(i * xStep + layer.position.x, (low[i] - xBase) * yStep + layer.position.y));
            }
            let upperLine = new MultiLine(layer.canvas, {
                color: '#C32512',
                lineWidth: 2,
            }, upperPoints);
            let middleLine = new MultiLine(layer.canvas, {
                color: '#136583',
                lineWidth: 2,
            }, middlePoints);
            let lowLine = new MultiLine(layer.canvas, {
                color: '#1DA343',
                lineWidth: 2,
            }, lowPoints);
            layer.addChild(upperLine, middleLine, lowLine);
        }
        return {
            max,
            min,
            drawFun,
            indicators: {
                upper: upperData,
                low: lowData,
                middle: middleData,
            },
        };
    },
    mike(layer, data, start, end) {
        const { N = 20 } = layer.params;
        let dataStart = start - N;
        if (dataStart < 0) {
            dataStart = 0;
        }
        const close = [], high = [], low = [];
        for (let i = dataStart; i < end; i++) {
            close.push(parseFloat(data[i].close));
            high.push(parseFloat(data[i].high));
            low.push(parseFloat(data[i].low));
        }

        const { WR, MR, SR, WS, MS, SS } = mike(high, low, close, N);
        const dataLen = (WR.length > end - start) ? (end - start) : WR.length;
        const WRData = [], MRData = [], SRData = [],
            WSData = [], MSData = [], SSData = [];
        let min = WR[WR.length - 1];
        let max = WR[WR.length -1];
        for (let i = 1; i <= dataLen; i++) {
            if (min > WR[WR.length - i]) {
                min = WR[WR.length - i];
            }
            if (max < WR[WR.length - i]) {
                max < WR[WR.length - i];
            }
            WRData.unshift(WR[WR.length - i]);
            if (min > MR[MR.length - i]) {
                min = MR[MR.length - i];
            }
            if (max < MR[MR.length - i]) {
                max = MR[MR.length - i];
            }
            MRData.unshift(MR[MR.length - i]);
            if (min > SR[SR.length - i]) {
                min = SR[SR.length - i];
            }
            if (max < SR[SR.length - i]) {
                max = SR[SR.length - i];
            }
            SRData.unshift(SR[SR.length - i]);
            if (min > WS[WS.length - i]) {
                min = WS[WS.length - i];
            }
            if (max < WS[WS.length - i]) {
                max = WS[WS.length - i];
            }
            WSData.unshift(WS[WS.length - i]);
            if (min > MS[MS.length - i]) {
                min = MS[MS.length - i];
            }
            if (max < MS[MS.length - i]) {
                max = MS[MS.length - i];
            }
            MSData.unshift(MS[MS.length - i]);
            if (min > SS[SS.length - i]) {
                min = SS[SS.length - i];
            }
            if (max < SS[SS.length - i]) {
                max = SS[SS.length - i];
            }
            SSData.unshift(SS[SS.length - i]);
        }
        const drawFun = (indicators, layer, options) => {
            const { WR, MR, SR, WS, MS, SS } = indicators;
            const { xStep, yStep, xBase } = options;
            let WRPoints = [], MRPoints = [], SRPoints = [],
                WSPoints = [], MSPoints = [], SSPoints = [];
            for (let i = 0; i < WR.length; i++) {
                WRPoints.push(new Point(i * xStep + layer.position.x, (WR[i] - xBase) * yStep + layer.position.y));
                MRPoints.push(new Point(i * xStep + layer.position.x, (MR[i] - xBase) * yStep + layer.position.y));
                SRPoints.push(new Point(i * xStep + layer.position.x, (SR[i] - xBase) * yStep + layer.position.y));
                WSPoints.push(new Point(i * xStep + layer.position.x, (WS[i] - xBase) * yStep + layer.position.y));
                MSPoints.push(new Point(i * xStep + layer.position.x, (MS[i] - xBase) * yStep + layer.position.y));
                SSPoints.push(new Point(i * xStep + layer.position.x, (SS[i] - xBase) * yStep + layer.position.y));
            }
            const WRLine = new MultiLine(layer.canvas, {
                color: '#3395A3',
                lineWidth: 2,
            }, WRPoints);
            const MRLine = new MultiLine(layer.canvas, {
                color: '#8532D4',
                lineWidth: 2,
            }, MRPoints);
            const SRLine = new MultiLine(layer.canvas, {
                color: '#1DA343',
                lineWidth: 2,
            }, SRPoints);
            const WSLine = new MultiLine(layer.canvas, {
                color: '#D41123',
                lineWidth: 2,
            }, WSPoints);
            const MSLine = new MultiLine(layer.canvas, {
                color: '#F3A124',
                lineWidth: 2,
            }, MSPoints);
            const SSLine = new MultiLine(layer.canvas, {
                color: '#2836B1',
                lineWidth: 2,
            }, SSPoints);
            layer.addChild(WRLine, MRLine, SRLine, WSLine, MSLine, SSLine);
        }
        return {
            max,
            min,
            drawFun,
            indicators: {
                WR: WRData,
                MR: MRData,
                SR: SRData,
                WS: WSData,
                MS: MSData,
                SS: SSData,
            },
        };
    },
    sar(layer, data, start, end) {
        const { N = 4, step = 2 , maxStep = 20 } = layer.params;
        let dataStart = start - N;
        if (dataStart < 0) {
            dataStart = 0;
        }
        const high = [], low = [];
        for (let i = dataStart; i < end; i++) {
            high.push(parseFloat(data[i].high));
            low.push(parseFloat(data[i].low));
        }
        const { SARofCurBar, SARofNextBar, Position, Transition } = sar(high, low, step, maxStep);
        const dataLen = (SARofNextBar.length > end - start) ? (end - start) : SARofNextBar.length;
        const SARofNextBarData = [], PositionData = [];
        let max = SARofNextBar[SARofNextBar.length - 1];
        let min = SARofNextBar[SARofNextBar.length - 1];
        for (let i = 1; i <= dataLen; i++) {
            if (min > SARofNextBar[SARofNextBar.length - i]) {
                min = SARofNextBar[SARofNextBar.length - i];
            }
            if (max < SARofNextBar[SARofNextBar.length - i]) {
                max = SARofNextBar[SARofNextBar.length - i];
            }
            SARofNextBarData.unshift(SARofNextBar[SARofNextBar.length - i]);
            PositionData.unshift(Position[Position.length - i]);
        }
        const drawFun = (indicators, layer, options) => {
            const { SARofNextBar, Position } = indicators;
            const { xStep, yStep, xBase } = options;
            for (let i = 0; i < SARofNextBar.length; i++) {
                let circle = new Circle(layer.canvas, {
                    color: Number(Position[i]) === -1 ? '#1DA343' : '#E43123',
                    radius: xStep / 6,
                    lineWidth: 2,
                    position: new Point(
                        layer.position.x + (i + 0.5) * xStep,
                        layer.position.y + (SARofNextBar[i] - xBase) * yStep,
                    ),
                    type: Circle.TYPE.STROKE,
                });
                layer.addChild(circle);
            }
        }
        return {
            max,
            min,
            drawFun,
            indicators: {
                SARofNextBar: SARofNextBarData,
                Position: PositionData,
            },
        };
    }
}

function defaultIndicatorCaculator(layer, data, start, end) {
    return indicatorFunctions[layer.name](layer, data, start, end);
}

export default class extends Layer {
    constructor(canvas, style, data = []) {
        super(canvas, style);
        this.data = data;
        this.dataStart = style.start || 0;
        this.dataEnd = style.end || this.data.length;
        this.name = style.name || 'ma';
        this.params = style.params || {};
        this.indicatorCaculator = style.indicatorCaculator || defaultIndicatorCaculator;
    }

    make() {
        this.childs.splice(0, this.childs.length);
        const { max, min, indicators, drawFun } = this.indicatorCaculator(this, this.data, this.dataStart, this.dataEnd);
        this.max = max;
        this.min = min;
        this.indicators = indicators;
        this.drawFun = drawFun;
        this.onMaked && this.onMaked({
            max,
            min,
            indicators,
        });
    }
}
